package controller;

import javax.swing.*;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import model.*;
import view.*;

/**
 * A program belépési pontja. Értesül a felhasználói eseményekről és annak
 * megfelelően módosítja a modellt.
 * 
 * 
 */
public class Controller {

	/**
	 * Példány a létrejött Controller osztályból
	 */
	private static Controller instance;

	/**
	 * Igaz állapotban a történt esetmények kijelzésre kerülnek a standard
	 * kimenetre Prototípusból örökölve. Debugcélokra tökéletes. Parancs sori
	 * argumentumként --verbose vagy -v flaggel állítható igazzá.
	 */
	public static boolean verbose = false;
	/**
	 * A kimentet kompozit elemek elérésí útjanák megadása, kiemelve a könnyebb
	 * változtatás érdekében. Parancssori argumentumként az értéke a
	 * --compositePath= után vagy a -cp= után adható meg.
	 */
	public static String compsitePath = "";

	private JFrame frame;
	private MouseAdapter mouseAdapter;
	private model.BreadBoard bb;
	private view.BreadBoardView bbView;

	/**
	 * A felület különböző módokban különböző interakciókra reagál.
	 * A lehetséges módon a nevük alapján értelemszerűen 
	 *
	 */
	public enum Mode { DESIGN, SIMULATION, ADD_WIRE, REMOVE_WIRE, ADD_BLOCK, REMOVE_BLOCK };	
	Mode mode = Mode.DESIGN;
	
	private BuildingBlock newBlock;
	public String compFullPath = "";
	
	private static view.InputView nextAddedWiresInput;
	private static view.OutputView nextAddedWiresOutput;
	private static view.InputView nextRemovedWiresInput;

	Timer timer;
	
	// /Menüsor:
	JMenuBar menuBar = new JMenuBar();
	JMenu file = new JMenu("File");
	//JMenu edit = new JMenu("Edit");
	JMenu help = new JMenu("Help");
	
	JLabel titleLabel = new javax.swing.JLabel("Digital System Builder");	
	JButton startButton = new javax.swing.JButton("Start Simulation");
	JButton stopButton = new javax.swing.JButton("Stop Simulation ");
	JButton stepButton = new javax.swing.JButton("Step");
	JSeparator jSeparator1 = new javax.swing.JSeparator();
	JButton addBlockButton = new javax.swing.JButton("Add Block");
	JButton removeBlockButton = new javax.swing.JButton("Remove Block");
	JButton addWireButton = new javax.swing.JButton("Add Wire");
	JButton removeWireButton = new javax.swing.JButton("Remove Wire");
	JSeparator jSeparator2 = new javax.swing.JSeparator();
	JSeparator jSeparator3 = new javax.swing.JSeparator();
	JLabel modeTitleLabel = new javax.swing.JLabel("Mode:");
	JLabel currentModeLabel = new javax.swing.JLabel();
	JButton resetCircuitButton = new javax.swing.JButton("Reset Circuit");
	JCheckBox checkStabilityCheckBox = new javax.swing.JCheckBox("Check Stability");

	Controller() {
		this.bb = new model.BreadBoard();
		this.bbView = new view.BreadBoardView(this.bb);
		BreadBoard.StabilityCheck = false;

		this.mouseAdapter = initializeMouseAdapter();
		this.bbView.addMouseListener(this.mouseAdapter);

		this.frame = intializeFrame();		
		this.frame.pack();
		frame.addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent e){
				Controller.getInstance().doStopSimulation();
				frame.dispose();
			}
		});
		currentModeLabel.setText("Design");
		try {
			/*
			 * Look and Feel az aktuális környezethez igazítása, aerora is
			 * működik.
			 */// N.B. also works for *nix & Mac
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
			SwingUtilities.updateComponentTreeUI(this.frame);
		} catch (Exception e) {
			System.err.println(e.getMessage());
		}
		this.frame.setVisible(true);
		Controller.instance = this;
	}

	/**
	 * Statikus metódusok számára és külső hivatkozások számára visszatér a Controller osztály létrehozott példányával
	 * @return
	 */
	public static Controller getInstance() {
		return instance;
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// Parancssori argumentumok értelmezeése:
		if (args.length != 0) {
			for (String str : args) {
				if (str.equalsIgnoreCase("--verbose") || str.equals("-v")) {
					Controller.verbose = true;
				}
				if (str.startsWith("--compositePath=")
						|| str.startsWith("-cp=")) {
					String compPath[] = str.split("=");
					Controller.compsitePath = (compPath.length > 0) ? compPath[1]
							: "";
				}

			}
		}
		Controller controller = new Controller();
		controller.toString();// Ezzel el tüntettem egy warningot, hogy a
								// controller nem volt sehol használva.
	}

	/**
	 * MouseAdapter a BreadBoardon való kattintások érzékelésére.
	 * 
	 * @return MouseAdapter aminek a metódusait felül lettek definiálva.
	 */
	private MouseAdapter initializeMouseAdapter() {
		return new MouseAdapter() {

			@Override
			public void mouseClicked(MouseEvent e) {
				super.mouseClicked(e);
				if (Mode.ADD_BLOCK.equals(mode)) {
					if (newBlock.getClass().equals(AndGate.class)) {
						AndGateView andgateview = new AndGateView(e.getX(),
								e.getY(), (AndGate) newBlock);
						bbView.getBlocks().add(andgateview);
						for (InputView iw : andgateview.inputViews) {
							bbView.add(iw);
						}
						bbView.add(andgateview.outputView);
						bbView.add(andgateview);
					} else if (newBlock.getClass().equals(OrGate.class)) {
						OrGateView orgateview = new OrGateView(e.getX(),
								e.getY(), (OrGate) newBlock);
						bbView.getBlocks().add(orgateview);
						for (InputView iw : orgateview.inputViews) {
							bbView.add(iw);
						}
						bbView.add(orgateview.outputView);
						bbView.add(orgateview);
						bbView.add(orgateview);
					} else if (newBlock.getClass().equals(NotGate.class)) {
						NotGateView notgateview = new NotGateView(e.getX(),
								e.getY(), (NotGate) newBlock);
						bbView.getBlocks().add(notgateview);
						bbView.add(notgateview.inputView);
						bbView.add(notgateview.outputView);
						bbView.add(notgateview);
					} else if (newBlock.getClass().equals(LED.class)) {
						LEDView ledview = new LEDView(e.getX(), e.getY(),
								(LED) newBlock);
						bbView.getBlocks().add(ledview);
						bbView.add(ledview);
					} else if (newBlock.getClass().equals(Switch.class)) {
						SwitchView switchview = new SwitchView(e.getX(),
								e.getY(), (Switch) newBlock);
						bbView.getBlocks().add(switchview);
						bbView.add(switchview);
					} else if (newBlock.getClass().equals(SequenceGen.class)) {
						SequenceGenView seqgenview = new SequenceGenView(
								e.getX(), e.getY(), (SequenceGen) newBlock);
						bbView.getBlocks().add(seqgenview);
						bbView.add(seqgenview.outputView);
						bbView.add(seqgenview);
					} else if (newBlock.getClass().equals(Oscilloscope.class)) {
						OscilloscopeView scopeview = new OscilloscopeView(
								e.getX(), e.getY(), (Oscilloscope) newBlock);
						bbView.getBlocks().add(scopeview);
						bbView.add(scopeview);
						bbView.add(scopeview.inputView);
					} else if (newBlock.getClass().equals(Composite.class)) {
						CompositeView compview = new CompositeView(e.getX(), e.getY(), (Composite) newBlock);
						bbView.getBlocks().add(compview);
						bbView.add(compview);
						for (InputView iw : compview.inputViews) {
							bbView.add(iw);
						}
						for (OutputView ow : compview.outputViews) {
							bbView.add(ow);
						}
					} else if (newBlock.getClass().equals(CompositePort.class)){
						CompositePortView compPortView = new CompositePortView(e.getX(), e.getY(), (CompositePort)newBlock);
						bbView.getBlocks().add(compPortView);
						bbView.add(compPortView);
						bbView.add(compPortView.inputView);
						bbView.add(compPortView.outputView);
					}
					bb.addBlock(newBlock);
					bbView.repaint();
					if(Controller.verbose){
						System.out.println("block added");
					}
				}

				currentModeLabel.setText("Design");
				mode = Mode.DESIGN;
				InputView.darken = false;
				OutputView.darken = false;
				if(Controller.verbose){
					System.out.println("controller mouse listener invoked");
				}
			}
		};
	}

	/**
	 * Esemény, ha a felhasználó egy OutputView-ra kattintott
	 * @param ow
	 */
	public void outputClicked(OutputView ow) {
		if (Mode.ADD_WIRE.equals(mode) && nextAddedWiresOutput == null) {
			nextAddedWiresOutput = ow;
			if(Controller.verbose){
				System.out.println("newWireOut set");
			}
			OutputView.darken = false;
			bbView.repaint();
		}

		if ((nextAddedWiresInput != null) && (nextAddedWiresOutput != null)) {
			doAddWire();
		}
	}

	/**
	 * Esemény, ha a felhasználó egy InputView-ra kattintott
	 * @param iw
	 */
	public void inputClicked(InputView iw) {
		if (Mode.ADD_WIRE.equals(mode) && nextAddedWiresInput == null) {
			nextAddedWiresInput = iw;
			if(Controller.verbose){
				System.out.println("newWireIn set");
			}
			InputView.darken = false;
			bbView.repaint();
		}

		if ((nextAddedWiresInput != null) && (nextAddedWiresOutput != null)) {
			doAddWire();
		}

		if (Mode.REMOVE_WIRE.equals(mode)
				&& nextRemovedWiresInput == null) {
			PopUp popUp = new PopUp(frame, "Are you sure to remove the wire?");
			popUp.setVisible(true);
			if (popUp != null && popUp.isOK) {
				nextRemovedWiresInput = iw;
				if(Controller.verbose){
					System.out.println("removingWireIn set");			
				}
				doRemoveWire();
			} else {
				return;
			}
			
		}
	}

	/**
	 * Esemény, ha a felhasználó SwitchView-ra kattintott
	 * @param sww
	 */
	public void switchClicked(SwitchView sww) {
		if (Mode.DESIGN.equals(mode) || Mode.SIMULATION.equals(mode)) {
			sww.getModel().setState(!sww.getModel().getOutput().getState());
			sww.getModel().processSignal();
		}
		bbView.repaint();
	}

	/**
	 * Esemény, ha a felhasználó egy View-ra kattintott
	 * Remove_block módonban eltávolítja az adott blokkot
	 * @param p
	 */
	public void blockClicked(Paintable p) {
		if (Mode.REMOVE_BLOCK.equals(mode)) {
			doRemoveBlock(p);
			bbView.repaint();
		}
	}

	/**
	 * Esemény, ha a felhasználó OscilloscopeView-ra kattintott
	 * @param scopeView
	 */
	public void scopeClicked(OscilloscopeView scopeView) {
		if (Mode.DESIGN.equals(mode)) {
			doGetMemory(scopeView.getModel());
		}
	}
	
	/**
	 * Esemény, ha felhasználó SequenceGenView-ra kattintott
	 * @param seqView
	 */
	public void seqGenClicked(SequenceGenView seqView) {
		if (Mode.DESIGN.equals(mode)) {
			doSetSequence(seqView.getModel());
		}
	}
	
	/**
	 * Esemény, ha a felhasználó CompositeView-ra kattintott
	 * @param compView
	 */
	public void compositeClicked(CompositeView compView){		
		if (Mode.DESIGN.equals(mode) || Mode.SIMULATION.equals(mode)){
			
			JFrame tmpFrame = new JFrame("Composite details: " + compView.getModel().getName());
			tmpFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
			tmpFrame.setBounds(100, 100, 600, 600);
			tmpFrame.setLayout(new java.awt.BorderLayout());
			
			BreadBoard innerBB = new BreadBoard();
			BreadBoardView innerBBView = new BreadBoardView(innerBB);
			innerBB.loadComposite(compView.getModel().fullpath, innerBBView);
			JPanel breadBoardView = innerBBView;
			breadBoardView.setVisible(true);
			tmpFrame.add(breadBoardView);
			tmpFrame.setVisible(true);
			innerBBView.repaint();
		}
	}
	
	/**
	 * Ha a modell-ben léptetés történt, ezzel jelzi, az eseményt, amelyre a Controller újrarajzolja a View-t
	 */
	public void stepped() {
		bbView.repaint();
		if(Controller.verbose){
			System.out.println("bbView repainted - step occured");
		}
	}
	
	/**
	 * Segédmetódus a különböző módok implementálásához
	 * Ki és bekapcsolja a felhasználói felület kattinthatóságát.
	 * Jelenleg Simulation módban csak a Stop simulation gomb aktív, minden más módban az összes gomb aktív
	 * @param state
	 */
	private void setUIEnabled(Boolean state){
		startButton.setEnabled(state);
		stepButton.setEnabled(state);
		addBlockButton.setEnabled(state);
		removeBlockButton.setEnabled(state);
		addWireButton.setEnabled(state);
		removeWireButton.setEnabled(state);
		file.setEnabled(state);
		resetCircuitButton.setEnabled(state);
		checkStabilityCheckBox.setEnabled(state);		
	}

	/**
	 * A parancsok megvalósításai:
	 */
	
	/**
	 * Oszcilloszkópra kattintás esetén popUp ablak a szkóp memóriájával
	 */
	protected void doGetMemory(Oscilloscope o) {
		PopUp popUp = new PopUp(this.frame, "Oscilloscope memory",
				PopUp.MEMORY_GETTER_SETTER);
		popUp.commonTextField.setText(o.getMemoryAsString());
		popUp.commonTextField.setEnabled(false);
		popUp.setVisible(true);
	}
	
	/**
	 * Szekvenciagenerátorra kattintás esetén szekvencia beállító popUp ablak
	 * @param seqGen
	 */
	protected void doSetSequence(SequenceGen seqGen) {
		boolean formatError = false;
		String message = null;
		String sequence = "";
		PopUp popUp;
		do {
			if (formatError) {
				message = "Wrong format! Please specify a sequence of 1s and 0s!";
			}
			popUp = new PopUp(frame, message, PopUp.SEQUENCE_SETTER);
			popUp.setVisible(true);
			if (popUp != null && popUp.isOK) {
				try {
					sequence = popUp.commonTextField.getText();
					java.util.regex.Pattern pattern = java.util.regex.Pattern
							.compile("(0|1)+");
					java.util.regex.Matcher matcher = pattern
							.matcher(sequence);
					if (!matcher.matches()) {
						throw new NumberFormatException();
					}
					seqGen.setSeq(sequence);
				} catch (NumberFormatException e) {
					formatError = true;
					continue;
				}
				formatError = false;
			} else {
				return;
			}
		} while (formatError);
		bbView.repaint();
	}

	/**
	 * Vezeték eltávolítást végrehajtó metódus
	 */
	protected void doRemoveWire() {
		if (Mode.DESIGN.equals(mode)) {
			InputView.darken = true;
			mode = Mode.REMOVE_WIRE;
			currentModeLabel.setText("Remove Wire");
			bbView.repaint();
			return;
		}
		
		if (Mode.REMOVE_WIRE.equals(mode) && nextRemovedWiresInput != null) {
			BuildingBlock block = nextRemovedWiresInput.getInBlock();
			WireView wireView = null;
			for (Paintable wv : bbView.wires) {
				Wire w = ((WireView) wv).getModel();
				if (w.getInBlock() == block) {
					wireView = (WireView) wv;
					break;
				}
			}
			bb.removeWire(wireView.getModel());
			bbView.wires.remove(wireView);
			nextRemovedWiresInput = null;

			currentModeLabel.setText("Design");
			mode = Mode.DESIGN;
			InputView.darken = false;
			bbView.repaint();
		}
	}

	/**
	 * Vezeték hozzáadást elvégző metódus
	 */
	protected void doAddWire() {
		if (Mode.ADD_WIRE.equals(mode)){
			if (nextAddedWiresInput.getModel().getConnectedOutput() == null) {
				Wire w = new Wire("wire" + System.currentTimeMillis(),
						nextAddedWiresInput.getInBlock(),
						nextAddedWiresInput.getModel(),
						nextAddedWiresOutput.getOutBlock(),
						nextAddedWiresOutput.getModel());
				WireView wv = new WireView(w, nextAddedWiresOutput.getX(),
						nextAddedWiresOutput.getY(),
						nextAddedWiresInput.getX(), nextAddedWiresInput.getY());
				bb.addWire(w);
				bbView.wires.add(wv);
				if(Controller.verbose){
					System.out.println("wire added");
				}
			}
			nextAddedWiresInput = null;
			nextAddedWiresOutput = null;
			mode = Mode.DESIGN;
			currentModeLabel.setText("Design");
			InputView.darken = false;
			OutputView.darken = false;
			bbView.repaint();
		}
	}

	/**
	 * Blokk eltávolítást végző metódus
	 * @param p
	 */
	protected void doRemoveBlock(Paintable p) {
		if (Mode.REMOVE_BLOCK.equals(mode)){
			PopUp popUp = new PopUp(frame, "Are you sure to remove the block?");
			popUp.setVisible(true);
			if (popUp != null && popUp.isOK) {
				BuildingBlock block = ((ModelProvider)p).getModel();
				
				java.util.ArrayList<Paintable> removableWires = new java.util.ArrayList<Paintable>();
				for(Paintable wv : bbView.wires){
					Wire w = ((WireView) wv).getModel();
					if (w.getInBlock() == ((ModelProvider)p).getModel()) {
						removableWires.add(wv);
					}
					if (w.getOutBlock() == ((ModelProvider)p).getModel()) {
						removableWires.add(wv);
					}
				}
				bbView.wires.removeAll(removableWires);
				
				bb.deleteBlock(block);
				bbView.getBlocks().remove(p);
				
				bbView.repaint();
				mode = Mode.DESIGN;
				currentModeLabel.setText("Design");
			} else {
				return;
			}
			

		}
	}

	/**
	 * Step gomb-ra egyet léptet az áramkörön
	 * @param step
	 */
	protected void doStep(int step) {
		try {
			bb.startSimulation(1);
		} catch (UnstableCircuitException e) {
			handleUnstableCircuit(e);
		} catch (InvalidCircuitException e) {
			handleInvalidCircuit(e);
		}
		bb.endSimulation();
	}

	/**
	 * Stop simulation gombra szimuláció leaállítása
	 */
	protected void doStopSimulation() {
		if(timer != null && Mode.SIMULATION.equals(mode)){
			if (timer.isRunning()){
				timer.stop();
				timer = null;
				bb.endSimulation();
				mode = Mode.DESIGN;
				currentModeLabel.setText("Design");
				setUIEnabled(true);
			}
			else{
				timer = null;
				bb.endSimulation();
				mode = Mode.DESIGN;
				currentModeLabel.setText("Design");
				setUIEnabled(true);
			}
		}
		else {
			bb.endSimulation();
			mode = Mode.DESIGN;
			currentModeLabel.setText("Design");
			setUIEnabled(true);
		}
	}

	/**
	 * Start simulation gomra szimuláció indítása folyamatos futásban, fél másodperces lépésközzel
	 * @param step
	 */
	protected void doStartSimulation(int step) {
		if (Mode.DESIGN.equals(mode)){					
			setUIEnabled(false);		
			mode = Mode.SIMULATION;
			currentModeLabel.setText("Simulation");
			ActionListener taskPerformer = new ActionListener() {
				public void actionPerformed(ActionEvent evt) {
					try {
						bb.startSimulation(1);
					} catch (UnstableCircuitException e) {
						handleUnstableCircuit(e);
					} catch (InvalidCircuitException e) {
						handleInvalidCircuit(e);
					}
					bbView.repaint();
				}
			};
	
			timer = new Timer(500, taskPerformer);
			timer.start();
		}
	}
	
	/**
	 * Instabil kapcsolás esetén jelentkező Exception lekezelése: szimuláció leállítása, resetelés és popUp ablak a felhasználónak
	 * @param e
	 */
	protected void handleUnstableCircuit(UnstableCircuitException e){
		doStopSimulation();
		bb.resetCircuit();
		BreadBoard.resetChangeCounter();
		new PopUp(this.frame, e.getMessage()).setVisible(true);
	}
	
	/**
	 * Érvénytelen kapcsolás esetén jelentkező Exception lekezelése: szimuláció leállítás és popUp ablak a felhasználónak
	 * @param e
	 */
	protected void handleInvalidCircuit(InvalidCircuitException e){
		doStopSimulation();
		new PopUp(this.frame, e.getMessage()).setVisible(true);
	}

	/**
	 * Help menüelemre meghívandó metódus
	 */
	protected void doHelp() {
		// TODO Auto-generated method stub

		new PopUp(this.frame, "Google it!").setVisible(true);
	}

	/**
	 * Load composite menüelemre hívandó metódus, feltölti a BreadBoardView-t a kiválasztott elemmel
	 * @param string
	 */
	protected void doLoadComposite(String string) {
		
		JFileChooser chooser = new JFileChooser(System.getProperty("java.class.path") + "\\..");
		javax.swing.filechooser.FileNameExtensionFilter filter = new javax.swing.filechooser.FileNameExtensionFilter(
	        "Composite View", "view", "view");
	    chooser.setFileFilter(filter);
	    int returnVal = chooser.showOpenDialog(this.frame);
	    if(returnVal == JFileChooser.APPROVE_OPTION) {
	    	String path = chooser.getSelectedFile().getPath();
	    	if(path.endsWith(".comp.view")){
	    		path = path.substring(0, path.length() - 5);
	    	}
	    	else if(! path.endsWith(".comp")){
	    		path = path + ".comp";
	    	}
    	    this.bbView.blocks.clear();
    	    this.bbView.inputs.clear();
    	    this.bbView.outputs.clear();
    	    this.bbView.wires.clear();
    	    this.bbView.removeAll();
    	    this.bbView.repaint();
    	    this.bb.loadComposite(path, this.bbView);
    	    this.bbView.repaint();
	    } else {
	    	return;
	    }
	}

	/**
	 * Save composite menüelemre hívandó metódus, elmenti a BreadBoard és BreadBoardView tartalmát a kijelölt fájlba
	 * Letörli a BreadBoardot 
	 * @param string
	 */
	protected void doSaveComposite(String string) {
	
		JFileChooser chooser = new JFileChooser(System.getProperty("java.class.path") + "\\..");
		javax.swing.filechooser.FileNameExtensionFilter filter = new javax.swing.filechooser.FileNameExtensionFilter(
	        "Composite View", "view", "view");
	    chooser.setFileFilter(filter);
	    int returnVal = chooser.showSaveDialog(this.frame);
	    if(returnVal == JFileChooser.APPROVE_OPTION) {
	    	String path = chooser.getSelectedFile().getPath();
	    	if(path.endsWith(".comp.view")){
	    		path = path.substring(0, path.length() - 5);
	    	}
	    	else if(! path.endsWith(".comp")){
	    		path = path + ".comp";
	    	}	    	
    	    this.bb.saveComposite(path, this.bbView);
    	    this.bbView.blocks.clear();
    	    this.bbView.inputs.clear();
    	    this.bbView.outputs.clear();
    	    this.bbView.wires.clear();
    	    this.bbView.removeAll();
    	    this.bbView.repaint();
	    } else {
	    	return;
	    }
	}

	/**
	 * New breadboard menüelemre hívandó metódus
	 * Letörli a BreadBoardot és BreadBoardViewt
	 */
	protected void doNewBreadBoard() {
		PopUp popUp = new PopUp(this.frame, "New BreadBoard");
		popUp.setVisible(true);
		if (popUp.isOK) {
			if(Controller.verbose){
				System.out.println("doNewBreadBoard called");
			}
			bb = new BreadBoard();
			bbView.blocks.clear();
			bbView.wires.clear();
			bbView.inputs.clear();
			bbView.outputs.clear();
			this.bbView.removeAll();
			bbView.repaint();
			//bbView = new BreadBoardView(bb);
		}

	}

	/**
	 * Exit menüelemre hívandó metódus: leállítja a szimulációt és kilép a programból
	 */
	protected void doExit() {
		doStopSimulation();
		System.exit(0);
	}

	/**
	 * Az ablak elkészítéséért felelős metódus.
	 * Eseménykezelők beregisztrálása
	 * @return
	 */
	private JFrame intializeFrame() {

		JFrame tmpFrame = new JFrame("Parizer - Digital System Builder");
		tmpFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		tmpFrame.setBounds(100, 100, 450, 250);
		tmpFrame.setLayout(new java.awt.BorderLayout());	
		
		JMenuItem newM = new JMenuItem("New");
		/**
		 * Anoním ActionListener ocsmány mint állat, de a modelbe nem raktunk
		 * ActionLisenert.
		 */
		newM.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(java.awt.event.ActionEvent evt) {
				doNewBreadBoard();
			}
		});
		JMenuItem save = new JMenuItem("Save Composite");
		save.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(java.awt.event.ActionEvent evt) {
				doSaveComposite("default.comp");
			}
		});
		JMenuItem load = new JMenuItem("Load Composite");
		load.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(java.awt.event.ActionEvent evt) {
				doLoadComposite("default.comp");
			}
		});
		JMenuItem exit = new JMenuItem("Exit");
		exit.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(java.awt.event.ActionEvent evt) {
				doExit();
			}
		});

		file.add(newM);
		file.add(save);
		file.add(load);
		file.add(exit);

		JMenuItem helpI = new JMenuItem("Help");
		helpI.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(java.awt.event.ActionEvent evt) {
				doHelp();
			}
		});
		help.add(helpI);

		menuBar.add(file);
		//menuBar.add(edit);
		menuBar.add(help);

		tmpFrame.add(menuBar, java.awt.BorderLayout.NORTH);

		// /Panel az ablak közepének megjelenítéséhez
		JPanel panel = new JPanel();

		/**
		 * BreadBoardView a panelhez adása:
		 */
		JPanel breadBoardView = this.bbView;

		// A panel követi az ablak nyúlását és lesz kiterjedése akkor is ha
		// nincs benne elem:
		javax.swing.GroupLayout breadBoardViewLayout = new javax.swing.GroupLayout(
				breadBoardView);
		breadBoardView.setLayout(breadBoardViewLayout);
		breadBoardViewLayout.setHorizontalGroup(breadBoardViewLayout
				.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
				.addGap(0, 600, Short.MAX_VALUE));
		breadBoardViewLayout.setVerticalGroup(breadBoardViewLayout
				.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
				.addGap(0, 400, Short.MAX_VALUE));
		titleLabel.setHorizontalAlignment(JLabel.CENTER);

		javax.swing.GroupLayout layout = new javax.swing.GroupLayout(panel);
		panel.setLayout(layout);
        layout.setHorizontalGroup(
                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(layout.createSequentialGroup()
                    .addContainerGap()
                    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                            .addComponent(checkStabilityCheckBox, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                            .addComponent(stopButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                            .addComponent(stepButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                            .addComponent(startButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                            .addComponent(resetCircuitButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                            .addComponent(addBlockButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                            .addComponent(removeBlockButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                            .addComponent(addWireButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                            .addComponent(removeWireButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                            .addComponent(jSeparator1)
                            .addComponent(jSeparator2)
                            .addComponent(modeTitleLabel)
                            .addComponent(currentModeLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 111, Short.MAX_VALUE)
                            .addComponent(jSeparator3)))
                    .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                    .addComponent(this.bbView, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addContainerGap())
            );
            layout.setVerticalGroup(
                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(layout.createSequentialGroup()
                    .addContainerGap()
                    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                        .addComponent(this.bbView, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                        .addGroup(layout.createSequentialGroup()
                            .addComponent(startButton)
                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                            .addComponent(stopButton)
                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                            .addComponent(stepButton)
                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                            .addComponent(resetCircuitButton)
                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                            .addComponent(checkStabilityCheckBox)
                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                            .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                            .addComponent(addBlockButton)
                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                            .addComponent(removeBlockButton)
                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                            .addComponent(jSeparator2, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addGap(2, 2, 2)
                            .addComponent(addWireButton)
                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                            .addComponent(removeWireButton)
                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                            .addComponent(jSeparator3, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                            .addComponent(modeTitleLabel)
                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                            .addComponent(currentModeLabel)))
                    .addContainerGap())
        );
		// A gombok ActionLisenerei:

		startButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {	
				BreadBoard.resetChangeCounter();
				doStartSimulation(1);
			}
		});
		stopButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				doStopSimulation();
			}
		});
		stepButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				doStep(1);
			}
		});
		addBlockButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				// PopUp-ok feldobása:
				int type = PopUp.AND_INDEX;
				int inputNum = 1;

				PopUp popUp = new PopUp(frame, "Add Block", PopUp.ADD_BLOCK);
				popUp.setVisible(true);
				if (popUp != null && popUp.isOK) {
					type = popUp.addComboBox.getSelectedIndex();
				} else {
					return;
				}

				if (type == PopUp.AND_INDEX || type == PopUp.OR_INDEX) {
					boolean formatError = false;
					String message = "";
					do {
						if (formatError) {
							message = "Wrong number! Positive integer required!";
						}
						popUp = new PopUp(frame, message, PopUp.INPUT_SETTER);
						popUp.setVisible(true);
						if (popUp != null && popUp.isOK) {
							String stepS = popUp.inputTextField.getText();
							try {
								inputNum = Integer.parseInt(stepS);
								if (inputNum < 1) {
									throw new NumberFormatException();
								}
							} catch (NumberFormatException e) {
								formatError = true;
								continue;
							}
							formatError = false;

							if (type == PopUp.AND_INDEX) {
								newBlock = new AndGate("AND"
										+ System.currentTimeMillis(), inputNum);
							} else if (type == PopUp.OR_INDEX) {
								newBlock = new OrGate("OR"
										+ System.currentTimeMillis(), inputNum);
							}
						} else {
							return;
						}
					} while (formatError);
				}

				if (type == PopUp.SWITCH_INDEX) {
					newBlock = new Switch("SWITCH"
							+ System.currentTimeMillis());
					((Switch) newBlock).setState(false);
				}

				if (type == PopUp.SEQUENCE_GENERATOR_INDEX) {
					newBlock = new SequenceGen("SEQGEN"
							+ System.currentTimeMillis());
					doSetSequence((SequenceGen)newBlock);					
				}

				if (type == PopUp.COMPOSITE_INDEX) {
					JFileChooser chooser = new JFileChooser(System.getProperty("java.class.path") + "\\..");
					javax.swing.filechooser.FileNameExtensionFilter filter = new javax.swing.filechooser.FileNameExtensionFilter(
				        "Composite Model", "comp");
				    chooser.setFileFilter(filter);
				    int returnVal = chooser.showOpenDialog(frame);
				    if(returnVal == JFileChooser.APPROVE_OPTION) {
				    	String path = chooser.getSelectedFile().getPath();
				    	if(path.endsWith(".comp.view")){
				    		path = path.substring(0, path.length() - 5);
				    	}
				    	newBlock = new Composite(path);
				    	((Composite)newBlock).fullpath = path;
				    } else {
				    	return;
				    }				
				}
				
				if (type == PopUp.COMPOSITEPORT_INDEX){
					newBlock = new CompositePort("COMP_PORT" + System.currentTimeMillis());
				}

				if (type == PopUp.OSCILLOSCOPE_INDEX) {
					newBlock = new Oscilloscope("SCOP"	+ System.currentTimeMillis());
				}

				if (type == PopUp.NOT_INDEX) {
					newBlock = new NotGate("NOT" + System.currentTimeMillis());
				}

				if (type == PopUp.LED_INDEX) {
					newBlock = new LED("LED" + System.currentTimeMillis());
				}
				currentModeLabel.setText("Add Block");
				mode = Mode.ADD_BLOCK;
				// doAddBlock(type, inputNum, state, sequence, compositePath);
			}
		});
		removeBlockButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				currentModeLabel.setText("Remove Block");
				mode = Mode.REMOVE_BLOCK;
			}
		});
		addWireButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				InputView.darken = true;
				OutputView.darken = true;
				currentModeLabel.setText("Add Wire");
				mode = Mode.ADD_WIRE;
				bbView.repaint();
			}
		});
		removeWireButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				InputView.darken = true;
				doRemoveWire();
			}
		});
		
		checkStabilityCheckBox.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				Boolean state = checkStabilityCheckBox.isSelected();
				BreadBoard.StabilityCheck = state;
				if(Controller.verbose){
					System.out.println("Stability check set to: " + state);
				}
			}
		});
		
		resetCircuitButton.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				bb.resetCircuit();
				if(Controller.verbose){
					System.out.println("Circuit reset");
				}
				bbView.repaint();
			}
		});

		tmpFrame.add(panel, java.awt.BorderLayout.CENTER);

		return tmpFrame;
	}

}